package com.leinbo.onedoc.utils

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.LevelFilter;
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.FileAppender;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.util.FileSize;
import ch.qos.logback.core.util.OptionHelper;
import org.slf4j.LoggerFactory;
import java.util.*
import java.util.concurrent.ConcurrentHashMap

/**
 * Created by sundaiyue on 2022/11/28 04:54
 */
object LoggerHolder {
    private val map: ConcurrentHashMap<String, Logger> = ConcurrentHashMap<String, Logger>()

    fun getLogger(n: String): Logger {
        var name = n
        var logger: Logger? = map[name]
        if (logger != null) {
            return logger
        }
        synchronized(LoggerHolder::class.java) {
            name = name.lowercase(Locale.getDefault())
            logger = map[name]
            if (logger != null) {
                return logger!!
            }
            logger = build(name)
            map.put(name, logger!!)
        }
        return logger!!
    }

    private fun build(name: String): Logger {
        val appender: RollingFileAppender<ILoggingEvent> =
            LoggerAppenderBuilder().createRollingFileAppender(name, Level.INFO)
        val context = LoggerFactory.getILoggerFactory() as LoggerContext
        val logger: Logger = context.getLogger(name)
        //设置不向上级打印信息 不继承祖先appender
        logger.isAdditive = false
        logger.addAppender(appender)
        return logger
    }

    // 日志输出
    internal class LoggerAppenderBuilder {
        fun createRollingFileAppender(name: String, level: Level): RollingFileAppender<ILoggingEvent> {
            val context = LoggerFactory.getILoggerFactory() as LoggerContext
            val logHome = "logs/"
            val appender = RollingFileAppender<ILoggingEvent>()

            //这里设置级别过滤器
            appender.addFilter(createLevelFilter(level))

            //设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
            // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
            appender.context = context

            //appender的name属性
            appender.name = name

            //设置文件名
            val fileName = "$logHome$name/$name"
            appender.setFile(OptionHelper.substVars("$fileName.log", context))

            // 追加
            appender.isAppend = true

            // 并发安全
            appender.isPrudent = false

            // 滚动策略
            appender.rollingPolicy = createSizeAndTimeBasedRollingPolicy(fileName, level, context, appender)

            // 编码器
            appender.setEncoder(createEncoder(context))

            // 启动
            appender.start()
            return appender
        }

        // 文件滚动策略
        private fun createSizeAndTimeBasedRollingPolicy(
            fileName: String,
            level: Level,
            context: LoggerContext,
            appender: FileAppender<ILoggingEvent>
        ): SizeAndTimeBasedRollingPolicy<*> {
            //设置文件创建时间及大小的类
            val policy: SizeAndTimeBasedRollingPolicy<*> = SizeAndTimeBasedRollingPolicy<Any?>()
            //文件名格式
            val fp: String = OptionHelper.substVars("$fileName-%d{yyyy-MM-dd}.log.%i", context)
            //最大日志文件大小
            policy.setMaxFileSize(FileSize.valueOf("10MB"))
            //设置文件名模式
            policy.fileNamePattern = fp
            //设置最大历史记录为50条
            policy.maxHistory = 50
            //总大小限制
            policy.setTotalSizeCap(FileSize.valueOf("32GB"))
            //设置父节点是appender
            policy.setParent(appender)
            //设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
            // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
            policy.setContext(context)
            policy.start()
            return policy
        }

        // 编码器
        private fun createEncoder(context: LoggerContext): PatternLayoutEncoder {
            val encoder = PatternLayoutEncoder()
            //设置上下文，每个logger都关联到logger上下文，默认上下文名称为default。
            // 但可以使用<contextName>设置成其他名字，用于区分不同应用程序的记录。一旦设置，不能修改。
            encoder.setContext(context)
            //设置格式
            encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n")
            encoder.start()
            return encoder
        }

        // 级别过滤器
        private fun createLevelFilter(level: Level): LevelFilter {
            val levelFilter = LevelFilter()
            levelFilter.setLevel(level)
            levelFilter.onMatch = FilterReply.ACCEPT
            levelFilter.onMismatch = FilterReply.DENY
            levelFilter.start()
            return levelFilter
        }
    }
}
